/*
 * @Author: zi.yang
 * @Date: 2022-11-27 01:23:34
 * @LastEditors: zi.yang
 * @LastEditTime: 2022-12-04 21:32:51
 * @Description: common
 * @FilePath: /promise/lib/utils/common.ts
 */
import Promise from '../index';
import resolvePromise from './resolvePromise';

export const noop: Noop = () => {};

export const toString = (val: unknown): String => Object.prototype.toString.call(val);

export const getType = (val: unknown): String => toString(val).slice(8, -1).toLowerCase();

export const isString = (val: unknown): Boolean => getType(val) === 'string';

export const isArray = (val: unknown): Boolean => Array.isArray(val);

export const isObject = (val: unknown): Boolean => typeof val === 'object';

export const isFunction = (val: unknown): Boolean => typeof val === 'function';

export const isNull = (val: unknown): Boolean => val === null;

export const isNotNull = (val: unknown): Boolean => !isNull(val);

// eslint-disable-next-line no-void
export const isNil = (val: unknown): Boolean => isNull(val) || val === void 0;

export const isNotNil = (val: unknown): Boolean => !isNil(val);

export const isIterable = (val: any): Boolean => {
  if (isNil(val)) return false;
  if (val[Symbol.iterator]) return true;
  return isString(val) || isArray(val);
};

export const isPromise = (val: any): Boolean => {
  if (isNil(val)) return false;
  if (val === Promise || val instanceof Promise || getType(val) === 'promise') {
    return true;
  }
  return (isObject(val) || isFunction(val)) && (isNotNil(val.then) || isNotNil(val.prototype.then));
};
/**
 * 和 Array.every 的区别： Array.every 不会对 <empty string> 进行遍历
 * @param array
 * @param total
 * @returns
 */
export const everyExists = (array: Array<any>, total?: number): Boolean => {
  if (total && array.length !== total) return false;
  let flag = true;
  for (let i = 0; i < array.length; i++) {
    if (isNil(array[i])) {
      flag = false;
      break;
    }
  }
  return flag;
};

export const convertFunc = <T>(func: T, defaultFunc: T | Noop = noop) => {
  return isFunction(func) ? func : defaultFunc;
};

export const getFulfilledFunc = <T>(onFulfilled: PromiseResolveFunction) => {
  return convertFunc(onFulfilled, (data: T): T => data);
};

// 2.2.7.4 If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
// 注意：这里不能 throw new Error(), 因为 2.2.7.4 中规定了必须返回相同的 reason, new Error 会导致 promise1 和 promise2 的 error 不相等
export const getRejectedFunc = (onRejected: PromiseRejectFunction) => {
  return convertFunc(onRejected, (error: unknown): never => {
    throw error;
  });
};

/**
 *
 * @param { Promise } promise Promise
 * @param { * } content value | reason
 * @param { Function } callback onFulfilled | onRejected
 */
export function thenableCallbackAdapter<T>(
  promise: ToyPromiseInterface<T>,
  content: unknown,
  callback: Function,
) {
  // 2.2.2.2: it must not be called before `promise` is fulfilled fulfilled after a delay
  // 2.2.3.2: it must not be called before `promise` is rejected rejected after a delay:
  const timer = setTimeout(() => {
    try {
      const x: ToyPromiseInterface<T> | unknown = callback(content);
      // 2.2.7.1 If either onFulfilled or onRejected returns a value x,
      // run the Promise Resolution Procedure [[Resolve]](promise2, x).
      resolvePromise(promise, x);
    } catch (err) {
      promise.reject(err);
    }
    clearTimeout(timer);
  }, 0);
}
